﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Windows.Media;

using Buddy.Common;

namespace WingIt
{
    [RevisionControlId("$Id: Logger.cs 684 2012-08-08 10:09:07Z Chinajade $")]
    public static class Logger
    {
        public delegate bool    ContractPredicateDelegate();
        public delegate string  StringProviderDelegate();

        public static void Write(string message)
        {
            Write(Colors.Green, message);
        }

        public static void Write(string message, params object[] args)
        {
            Write(Colors.Green, message, args);
        }

        public static void Write(Color clr, string message, params object[] args)
        {
            Logging.Write(clr, "[WingIt] " + message, args);
        }

        /// <summary>
        /// <para>This is an efficent poor man's vehicle for reporting contract violations in methods.</para>
        /// <para>If the provided ISCONTRACTOKAY returns true, no action is taken.
        /// If false, a diagnostic message--given by the STRINGPROVIDERDELEGATE--is emitted to the log.</para>
        /// <para>The emitted information can then be used to locate and repair the code misusing the interface. </para>
        /// <para>Notes:<list type="bullet">
        /// <item><description><para> * The interface is built in terms of a StringProviderDelegate, so we don't pay a performance penalty
        /// to build an error message that is not used when ISCONTRACTOKAY is true.</para></description></item>
        /// <item><description><para> * The .NET 4.0 Contract support is insufficient due to the way parts of the project is dynamically compiled
        /// at run time.</para></description></item>
        /// </list></para>
        /// </summary>
        /// <param name="isContractOkay"></param>
        /// <param name="message"></param>
        /// <param name="args"></param>
        ///  30Jun2012-15:58UTC chinajade
        ///  NB: We could provide a second interface to ContractRequires() that is slightly more convenient for static string use.
        ///  But *please* don't!  If helps maintainers to not make mistakes if they see the use of this interface consistently
        ///  throughout the code.
        public static void  ContractRequires(ContractPredicateDelegate isContractOkay, StringProviderDelegate stringProviderDelegate = null)
        {
            if (!isContractOkay())
            {
                // TODO: (Future enhancement) Build a string representation of isContractOkay if stringProviderDelegate is null
                string      message = stringProviderDelegate() ?? "NO MESSAGE PROVIDED";
                StackTrace  trace   = new StackTrace(1);

                Logging.WriteDiagnostic(Colors.DarkRed, "[WingIt, CONTRACT VIOLATION] {0}\nLocation:\n{1}",
                                        message, trace.ToString());
            }
        }

        /// <summary>
        /// <para>For DEBUG USE ONLY--don't use in production code! (Almost exclusively used by DebuggingTools methods.)</para>
        /// </summary>
        /// <param name="message"></param>
        /// <param name="args"></param>
        public static void Debug(string message, params object[] args)
        {
            string      formattedMessage    = string.Format(message, args);

            Logging.Write(Colors.Blue, "[WingIt, DEBUG] {0}", formattedMessage);
        }

        /// <summary>
        /// MaintenanceErrors occur as a result of incorrect code maintenance.  There is usually no corrective
        /// action a user can perform in the field for these types of errors.
        /// </summary>
        /// <param name="message"></param>
        /// <param name="args"></param>
        ///  30Jun2012-15:58UTC chinajade
        public static void MaintenanceError(string message, params object[] args)
        {
            string          formattedMessage    = string.Format(message, args);
            StackTrace      trace               = new StackTrace(1);

            Logging.WriteDiagnostic(Colors.DarkRed, "[WingIt, MAINTENANCE ERROR] {0}\nLocation:\n{1}",
                                    formattedMessage, trace.ToString());
        }

        /// <summary>
        /// <para>Used to notify of problems where corrective (fallback) actions are possible.</para>
        /// </summary>
        /// <param name="message"></param>
        /// <param name="args"></param>
        public static void Warning(string message, params object[] args)
        {
            string      formattedMessage    = string.Format(message, args);

            Logging.Write(Colors.Blue, "[WingIt, WARNING] {0}", formattedMessage);
        }

        #region Helpers
        /// <summary>
        /// <para>Returns the name of the method that calls this function. If SHOWDECLARINGTYPE is true, the scoped method name is returned;
        /// otherwise, the undecorated name is returned.</para>
        /// <para>This is useful when emitting log messages.</para>
        /// </summary>
        /// <returns></returns>
        ///  7Jul2012-20:26UTC chinajade
        public static string    GetMyMethodName(bool  showDeclaringType   = false)
        {
            var method  = (new StackTrace(1)).GetFrame(0).GetMethod();

            if (showDeclaringType)
                { return (method.DeclaringType + "." + method.Name); }

            return (method.Name);
        }

        public static string    GetName(Expression<Func<object>> expression)
        {
            MemberExpression body = expression.Body as MemberExpression;

            if (body != null)
                { return (body.Member.Name); }

            // Try as unary...
            body = ((UnaryExpression)expression.Body).Operand as MemberExpression;

            return (body.Member.Name);
        } 
        #endregion  // Helpers
    }
}
